Files
harmony/docs/guides/harmony-auth-cli.md

11 KiB

Harmony Auth CLI

Status: read-only commands implemented. Mutation commands follow the ADR-027 group migration.

harmony-auth inspects and manages the relationship between Zitadel identities and OpenBao access. It presents tenants, identities, and Harmony permissions first. JWT roles, policy names, subject claims, and HCL remain available through advanced output.

The CLI is the preferred interface while the web UI matures. Both interfaces use the same harmony_auth operations and return the same effective access.

Mental model

The CLI has two main views:

  • identity: who an identity is and what it can access
  • tenant: who can access a tenant or project

Harmony permissions are the primary authorization vocabulary:

Permission Intended identity Effect
tenant-admin Human Read, create, change, and delete secrets in a tenant or project
cd-deployer Service account Read deployment secrets in one project
read-only Human or service account Read secrets in a tenant or project

Existing OpenBao policies that do not correspond to a Harmony assignment are shown as imported access. The CLI does not rename, rewrite, or hide them.

Command tree

harmony-auth
├── connection check
├── identity list
├── identity show <subject-id>
├── tenant list
└── tenant show <tenant>

There are no flat aliases. harmony-auth list is not valid.

Connection and credentials

Every command except --help and --version requires:

Flag Environment Meaning
--zitadel-url ZITADEL_URL Zitadel base URL
--openbao-url OPENBAO_URL OpenBao base URL
none ZITADEL_PAT Zitadel service-account PAT
none OPENBAO_TOKEN Temporary OpenBao administrator token

Secrets are environment-only because command-line arguments remain in shell history and may be visible in the process list. Secret values never appear in help output, normal output, JSON, or logs.

Example:

export ZITADEL_URL=https://sso.example.com
export ZITADEL_PAT=...
export OPENBAO_URL=https://secrets.example.com
export OPENBAO_TOKEN=...

harmony-auth connection check

The CLI does not persist profiles or credentials. Browser profile storage and session credential refresh remain web UI concerns.

connection check attempts both backends even when one fails. It reports each status without printing provider response bodies:

Zitadel  connected
OpenBao  connected

Identity commands

List identities

harmony-auth identity list
harmony-auth identity list --search folk
harmony-auth identity list --kind human
harmony-auth identity list --tenant devsights
harmony-auth identity list --tenant devsights --kind service

Filters combine with AND semantics. --search matches display name, login, or email. --kind accepts human or service. Tenant matching uses parsed assignment and imported-policy scopes, not string-prefix matching.

Imported scopes are recognized only from wildcard roots:

  • <mount>/data/<tenant>/* is tenant-wide.
  • <mount>/data/<tenant>/<project>/* is project-specific.
  • Exact secret paths and wildcard paths below a project are custom access. They remain visible but do not affect tenant filters or summaries.

Tenant and project components must pass the same slug validation as managed Harmony scopes. harmony_auth returns both the raw paths and parsed scopes; frontends never infer scopes themselves.

Human output keeps the tenant visible:

ACTIVE  HUMAN    Alice Example  alice@example.com
        subject  241696899342475267
        devsights                 Tenant Admin

ACTIVE  SERVICE  Folk CD        folk-cd
        subject  241697058442100739
        devsights/folk-timesheet CD Deployer

An identity without recognized access is still listed with No access.

Show an identity

harmony-auth identity show 241696899342475267

Default output includes identity metadata, Harmony assignments, and imported access:

Alice Example
  Subject: 241696899342475267
  Login:   alice@example.com
  Kind:    Human
  Status:  Active

Harmony permissions
  019b...  Tenant Admin  devsights

Imported OpenBao access
  legacy-folk-reader  devsights/folk-timesheet  Read secrets

Use --advanced to inspect implementation details:

harmony-auth identity show 241696899342475267 --advanced

Advanced output adds matching JWT roles, bound subject, audiences, attached policy names, and the exact HCL returned by OpenBao. A built-in or inaccessible policy remains listed with Policy body unavailable.

Planned group-based mutations

The first release does not grant or revoke access. Existing per-subject JWT roles are discovery input, not a writable authorization model.

ADR-027 makes Zitadel roles named <tenant>:owner, <tenant>:deployer, and <tenant>:viewer authoritative. One shared OpenBao JWT role reads the groups claim, and OpenBao external groups attach policies. Mutation commands ship only after harmony_auth implements that model end to end.

Grant a permission (planned)

harmony-auth identity grant 241696899342475267 \
  --permission tenant-admin \
  --tenant devsights

Grant is review-only by default:

Plan
  Identity:   Alice Example (241696899342475267)
  Permission: Tenant Admin
  Scope:      devsights
  Effect:     Read, create, change, and delete secrets

No changes applied. Re-run with --apply to continue.

Apply the reviewed request explicitly:

harmony-auth identity grant 241696899342475267 \
  --permission tenant-admin \
  --tenant devsights \
  --apply

cd-deployer requires --project. Other permissions accept an optional project. Permission applicability and scope validation come from harmony_auth; the CLI does not duplicate those rules.

Applying a grant changes the Zitadel role assignment. It does not create or edit a per-subject OpenBao JWT role. Applying the same grant twice must report changed: false.

Revoke a permission (planned)

The assignment ID comes from identity show:

harmony-auth identity revoke 241696899342475267 019b...

Revoke is also review-only by default. It shows identity, permission, scope, and the warning that already-issued OpenBao tokens remain valid until expiry or revocation. --apply performs the removal:

harmony-auth identity revoke 241696899342475267 019b... --apply

Imported per-subject access cannot be revoked through this command because it is outside the ADR-027 group model. Its OpenBao policy name remains visible for manual migration.

Tenant commands

List tenants

harmony-auth tenant list

Tenants and projects are discovered from managed assignments and recognized OpenBao policy paths:

TENANT     PROJECT          HUMANS  SERVICES
devsights  All projects     2       0
devsights  folk-timesheet   1       1
detexion   harmony-fleet    0       2

Show a tenant

harmony-auth tenant show devsights
harmony-auth tenant show devsights --project folk-timesheet

Output lists matching scopes and identities with their access. With --project, tenant-wide access and access to that exact project are included; other projects are excluded:

devsights/folk-timesheet
  Alice Example  Human    Read-only
  Folk CD        Service  CD Deployer

This is an authorization view, not a secret-value browser. Secret listing, creation, update, and reveal are outside the first release.

JSON output

Every implemented command accepts --json. JSON is written to stdout; diagnostics and logs are written to stderr. The envelope is versioned:

{
  "schema_version": 1,
  "command": "identity.list",
  "result": {}
}

The first release uses these result shapes:

Command result fields
connection check zitadel: { connected }, openbao: { connected }
identity list identities: [{ identity, access }]
identity show identity, access
tenant list tenants: [{ scope, humans, services }]
tenant show tenant, project, identities: [{ identity, access }]

identity contains subject_id, kind, display_name, login_name, email, and active. Identity kinds are human and service; email is a string or null.

scope contains tenant and project, where project is a string or null. An assignment contains id, subject_id, permission, scope, policy_name, and created_at. IDs are UUID strings, timestamps are RFC 3339, and permissions are tenant_admin, cd_deployer, or read_only.

access contains:

  • assignments: assignment objects as defined above
  • imported: objects with role_name, policy_name, secret_paths, scopes, and effect
  • roles: objects with name, subject_id, bound_audiences, and policies

Each role policy contains name and body. Policy bodies are strings only for identity show --advanced; otherwise body is null. Imported secret_paths, audiences, and scopes are arrays. effect is the plain-language interpretation returned by harmony_auth.

connection check always returns both status objects and exits 1 when either connected value is false. A top-level error is used only when the command cannot produce those statuses.

Domain and backend errors use stdout when --json is active:

{
  "schema_version": 1,
  "command": "identity.show",
  "error": {
    "kind": "not_found",
    "message": "identity not found"
  }
}

Error kinds are not_found, invalid, and backend. Clap usage errors remain on stderr because command parsing fails before JSON dispatch.

Planned grant and revoke results will add applied and changed when those commands are implemented. Their JSON schema is not frozen by this release.

Exit and error behavior

Exit Meaning
0 Query completed, plan produced, or mutation applied/no-op
2 Invalid command, missing connection value, not found, or invalid input
1 Zitadel or OpenBao request failed

Errors name the failed operation and backend but do not print credentials, provider response bodies, or stack traces. RUST_LOG=info enables operation logs on stderr.

Architecture boundary

harmony_auth_ui  ─┐
                  ├──> harmony_auth ──> Zitadel + OpenBao
harmony_auth_cli ─┘

harmony_auth owns:

  • identities, scopes, permissions, assignments, roles, policies, and plans
  • Zitadel identity discovery
  • OpenBao role and policy discovery
  • tenant and identity access queries
  • permission applicability and scope validation
  • assignment discovery and current legacy assignment operations used by the UI
  • provider request construction and response interpretation

harmony_auth_cli owns:

  • Clap arguments and environment mapping
  • terminal and JSON rendering
  • binary exit codes and logging setup

harmony_auth_ui owns HTTP routes, browser sessions, cookies, forms, HTML, CSS, and browser JavaScript. Neither frontend may parse OpenBao policies, infer tenants, reconcile JWT roles, or implement permission rules.

First-release limits

  • Authorization discovers existing per-subject JWT roles as imported access.
  • Grant and revoke wait for Zitadel role and OpenBao external-group operations.
  • The CLI does not create Zitadel identities.
  • The CLI does not provide a generic OpenBao policy editor.
  • Tenant administrators are not yet authenticated as constrained actors; the supplied OpenBao token determines backend authority.
  • The CLI does not store profiles or credentials.