Skip to main content

Loha CLI

The Loha CLI is a single static binary for macOS, Linux and Windows. It drives the same /api/v1 surface as the dashboard, so anything you do in the browser you can script from a terminal, a CI job, or an agent.

Install​

curl -fsSL https://s3th-api-a.inet.co.th/nopadolsooktiebqli1:loha-cli/install.sh | sh
loha version

The script detects your OS and architecture, downloads the latest release, verifies its checksum, and installs loha to /usr/local/bin (or ~/.local/bin when that isn't writable). Set LOHA_VERSION to pin a version or LOHA_INSTALL_DIR to choose the directory. On Windows, download loha-windows-amd64.exe from https://s3th-api-a.inet.co.th/nopadolsooktiebqli1:loha-cli/<version>/; the latest version number is in https://s3th-api-a.inet.co.th/nopadolsooktiebqli1:loha-cli/stable.txt.

Keep it current with:

loha update            # replace the binary with the latest release
loha update --check # only report whether an update is available

Commands​

Read commands (builds, build get, logs, repos, repos discover, connections list) accept --json for stable, machine-readable output: the path for scripting and agents.

  • loha login: save a workspace credential (a personal access token or service account key) as a named context. Members, admins and owners can create a personal access token in Settings → API Keys (guests cannot), where member self-service is enabled for the workspace.
  • loha whoami: show the active token's workspace, identity, and scopes
  • loha workspace list / loha workspace use <context>: list / switch contexts
  • loha init (alias loha scaffold): scaffold a starter .loha.yml
  • loha validate [file]: validate a .loha.yml against the server parser (defaults to ./.loha.yml)
  • loha plan [--branch <b>] [--event <e>] [--json]: show what the cluster would run for a .loha.yml, without running anything (see loha plan)
  • loha run --local [--job <id>] [--secret NAME] [--env-file <path>] [--json]: run the plan's jobs in Docker or Podman containers on your machine, with logs and artifacts in .loha/local-runs/ (see Run your pipeline locally)
  • loha connections list: list the workspace's SCM connections
  • loha connections add: store an SCM connection from a Git host token
  • loha repos: list repositories in the active workspace
  • loha repos discover --connection <id>: browse remote repos reachable through a connection
  • loha repos add: import a repository into the workspace (with --connection + --full-name, --html-url and the default branch are resolved automatically via discovery)
  • loha link --repo <id>: create a trigger for a repo
  • loha onboard: import a repo, create its trigger, and optionally run the first build in one step (see One-shot onboarding)
  • loha deploy [--repo <id>] [--watch]: fire a manual run for a trigger (repo inferred from the git remote when omitted; optionally wait for it)
  • loha builds [filters] [--json]: list recent builds (filter by --repo/--status/--branch/--since/--until)
  • loha build get <id> [--watch]: show a build's detail and step breakdown
  • loha logs <id> [--follow]: fetch a build's logs (paginated; --follow polls until the build finishes)
  • loha cancel <id>: cancel a PENDING/QUEUED/RUNNING build
  • loha releases [filters] [--json]: list releases: which commit produced which image, and its digest (filter by --repo/--project/--build)
  • loha images verify <repository@sha256:digest>: verify an image's Loha signature and provenance with your local cosign
  • loha images policy --engine sigstore [--mode audit|enforce]: download the workspace's admission policy to apply to your own cluster
  • loha update: self-update to the latest released version
  • loha version: print the CLI version
Running in CI/CD?

Use a service account key instead of a personal token, and pass it via the LOHA_TOKEN environment variable: no loha login and no config file needed. See Using the CLI in CI/CD.

Authentication​

On your laptop, authenticate once and the CLI remembers it. Create a token in the dashboard under Settings → API Keys, then log in with the API URL of this environment:

loha login --api-url https://app.loha.longtumlab.com --token loha_xxxxxxxxxxxxxxxxxxxx
# optionally name the context (handy if you use more than one workspace):
loha login --api-url https://app.loha.longtumlab.com --token loha_… --context work
loha whoami
Always pass --api-url

Pass --api-url https://app.loha.longtumlab.com (or set LOHA_API_URL) on the first login. The CLI's built-in default endpoint is not this environment, so a login without it fails. The URL is saved with the context, so later commands don't need it.

The credential is written to a config file at ~/.config/loha/config.yaml (mode 0600, honoring XDG_CONFIG_HOME), not an OS keychain. Each context pairs a workspace-scoped token with an API URL; loha workspace use <context> switches between them.

In CI there's no config file: set LOHA_TOKEN and LOHA_API_URL=https://app.loha.longtumlab.com and every command runs headless. See Using the CLI in CI/CD.

How commands map to the API​

Each command is a thin wrapper over the /api/v1 surface. You can drive the same flow with curl today.

CommandEndpoint(s)Scope
loha login / loha whoamiGET /api/v1/whoami—
loha validatePOST /api/v1/pipelines/validatepipelines:read
loha planPOST /api/v1/pipelines/planpipelines:read (+ variables:values:read for plain variable values)
loha run --localPOST /api/v1/pipelines/plan (the only request; steps run locally)pipelines:read (+ variables:values:read for plain variable values)
loha connections listGET /api/v1/scm-connectionsconnections:read
loha connections addPOST /api/v1/scm-connectionsconnections:write
loha reposGET /api/v1/reposrepos:read
loha repos discoverGET /api/v1/scm-connections/{id}/reposrepos:read
loha repos addPOST /api/v1/reposrepos:write
loha linkPOST /api/v1/triggers/createtriggers:write
loha onboardPOST /api/v1/repos, POST /api/v1/triggers/create, then a runrepos:read, repos:write, triggers:write (+ triggers:run with --deploy)
loha deployGET /api/v1/triggers (with --repo), POST /api/v1/triggers/{id}/runpipelines:read, triggers:run
loha buildsGET /api/v1/buildsbuilds:read
loha build get / --watchGET /api/v1/builds/{id}builds:read
loha logsGET /api/v1/builds/{id}/logsbuilds:read
loha cancelDELETE /api/v1/builds/{id}builds:write
loha releasesGET /api/v1/releasesrepos:read
loha images policyGET /api/v1/security/admission-policysecurity:read

(loha workspace …, loha init, loha update, and loha version are local: they don't call the API.)

Onboarding a repo from the terminal​

Connecting a repo no longer requires the dashboard. The only manual step is creating the SCM token in your Git host (e.g. a GitLab PAT with read_repository); everything after is headless:

loha connections add --name "Company GitLab" --provider gitlab \
--api-url https://gitlab.example.com --token <pat> # connections:write
loha repos discover --connection <connection-id> # pick a repo
loha repos add --connection <connection-id> \
--full-name group/project # html-url + branch resolved via discovery
loha link --repo <repository-id> # create a trigger
loha deploy --repo <repository-id> --watch # ship and wait

One-shot onboarding​

loha onboard collapses the import → link → run steps into a single command. Once you have an SCM connection, point it at a repo and (optionally) ship the first build in one go:

# fully headless: import, create a "deploy" trigger, run it, and wait
loha onboard --connection <connection-id> --repo group/project --deploy --watch

It resolves the repo's web URL and default branch through the connection, imports it, scaffolds a manual trigger that builds the repo's .loha.yml, and, with --deploy (or --watch, which implies --deploy), fires the first build. Each step prints as it completes (Repository imported: → Trigger created: → Build queued:) so a mid-flow failure is unambiguous about which step failed. Without --deploy, it ends with a Next: hint pointing at loha deploy --repo <id>.

Flags:

FlagPurpose
--connection <id>SCM connection to import through
--repo group/projectrepository path (full name) to import
--nametrigger name (default deploy)
--branchbranch to build (default: repo's default branch)
--config-pathpipeline config path (default .loha.yml)
--deployrun the trigger after linking
--watchpoll the build until it finishes (implies --deploy); exits non-zero on failure
--var KEY=VALUEruntime variable injected as step env, repeatable (only used when deploying)
--jsonprint the created repo + trigger + run as a JSON summary

Run interactively by omitting --connection/--repo on a terminal. It lists your connections and their discoverable repos and asks you to pick. For non-interactive use (a pipe, or --json), both flags are required and a clear error is returned instead of blocking on a prompt.

Watching builds & exit codes (agents / CI)​

--watch (on deploy and build get) polls until the build reaches a terminal status and exits non-zero when it ends FAILED/CANCELLED/TIMEOUT, so a CI step or autonomous agent can branch on the exit code without parsing output. Combine with --json on the read commands for structured results, and loha logs <id> --follow to stream a running build's logs to completion.

Deploy semantics​

loha deploy fires a manual run for a trigger, which builds from the .loha.yml committed to your repository and ships through Loha's GitOps (ArgoCD) path, so commit and push your pipeline first.

The trigger is resolved in three escalating steps:

  1. --trigger <id> runs that trigger directly.
  2. --repo <id> resolves the repo's enabled, manually-runnable triggers. When exactly one matches, the CLI runs it directly: the common case (one repo, one deploy trigger) needs no extra typing. When more than one matches it stays explicit: the candidates are listed and you must re-run with --trigger <id> so it's always unambiguous which trigger fired. --yes/-y skips any confirmation on the single-trigger case (it already auto-runs by default).
  3. Neither flag: run loha deploy from inside a repo's checkout and the CLI infers the repository from the local git remote (origin), then resolves its trigger as in step 2. If the cwd isn't a git repo, or the remote matches no onboarded Loha repo, the error tells you to pass --repo <id> explicitly or check loha repos.

Optional --branch / --commit flags override the git ref; otherwise the trigger's own configuration decides.

Deploying an uncommitted local working directory (uploading a build context, vercel deploy-style) is a separate, larger capability and is not part of this flow.

Architecture (for contributors)​

  • Distribution: a single static Go binary (like gh/vercel), no runtime dependency on the user's machine.
  • Client: a thin typed wrapper over the published /api/v1 surface; every request is bearer-token authenticated, so the CLI stays in lockstep with the API contract.
  • Auth: loha login saves a token to ~/.config/loha/config.yaml and sends it as a bearer token; the CLI only ever talks to /api/v1.

On the roadmap​

  • Browser login: loha login with no token opens the dashboard, lets you pick a workspace, and returns a scoped, expiring token (no manual paste).

Where to give feedback​

CLI scope is still being shaped. If you have a use case we haven't covered, file an issue in the public loha-apps repo or reach out to the team.